﻿using System.Windows.Controls;
using System.ComponentModel;
using System.Windows.Input;

namespace WPFCustomTextboxControls
{
    public class MaskedTextBox : TextBox
    {
        #region Member
        protected MaskedTextProvider _mask = null;
        protected bool _ignoreSpaceKey;
        protected bool _insertKeyIsOn;
        protected bool _newTextIsOK;
        protected bool _stayInFocusUntilValid;
        #endregion

        #region Properties
        /// <summary>
        /// Festlegen der Anzeigemaske die genutzt werden soll, diese wird als String "übergeben"
        /// </summary>
        public string Mask
        {
            get
            {
                if (_mask != null)
                    return _mask.Mask;

                return string.Empty;
            }
            set
            {
                _mask = new MaskedTextProvider(value);
                this.Text = _mask.ToDisplayString();
            }
        }

        /// <summary>
        /// Gibt an ob die Leertaste ignoriert werden soll, bei der Eingabe.
        /// </summary>
        public bool IgnoreSpaceKey
        {
            get { return _ignoreSpaceKey; }
            set { _ignoreSpaceKey = value; }
        }

        /// <summary>
        /// Gibt an ob der InsertKey eingeschalten ist.
        /// </summary>
        public bool InsertKeyIsOn
        {
            get { return _insertKeyIsOn; }
            set { _insertKeyIsOn = value; }
        }

        /// <summary>
        /// Gibt an ob der zuletzt eingegebene Text OK war und der Eingabemaske entsprach.
        /// </summary>
        public bool NewTextIsOk
        {
            get { return _newTextIsOK; }
            set { _newTextIsOK = value; }
        }

        /// <summary>
        /// Gibt an ob der Focus im Eingabefeld bleiben soll wenn es sich um eine komplett "richtige" Eingabe handelt
        /// </summary>
        public bool StayInFocusUntilValid
        {
            get { return _stayInFocusUntilValid; }
            set { _stayInFocusUntilValid = value; }
        }
        #endregion

        #region Functions/Subs
        /// <summary>
        /// Klicken des übergebenen Key, ohne das man dafür die Tastatur "nutzen" muss.
        /// </summary>
        /// <param name="key">Der Key der "geklickt" werden soll</param>
        protected void PressKey(Key key)
        {
            //"Klicken" des übergeben Keys auf der Tastatur ohne das eine Nutzereingabe stattgefunden haben muß.
            KeyEventArgs eInsertBack = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key);
            eInsertBack.RoutedEvent = KeyDownEvent;
            InputManager.Current.ProcessInput(eInsertBack);
        }
        #endregion

        #region Events
        /// <summary>
        /// Prüfen was für eine Taste geklickt wurde und hier einige Tasten direkt "deaktivieren"
        /// </summary>
        protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
        {
            //Austellen, das Text makiert werden kann
            if (this.SelectionLength > 1)
            {
                this.SelectionLength = 0;
                e.Handled = true;
            }

            //Überrprüfen welche Tasten noch deaktivert werden sollen und somit keine Aktion in der Textbox ausführen sollen
            if (e.Key == Key.Insert || e.Key == Key.Delete || e.Key == Key.Back || (e.Key == Key.Space && IgnoreSpaceKey))
            {
                e.Handled = true;
            }

            base.OnPreviewKeyDown(e);
        }

        /// <summary>
        /// Wird ausgelöst wenn das Textfeld den Fokus erhält
        /// </summary>
        protected override void OnGotFocus(System.Windows.RoutedEventArgs e)
        {
            //Beim Focus des Controls, muss der Insert Mode angeschalten werden
            base.OnGotFocus(e);
            if (!InsertKeyIsOn)
            {
                PressKey(Key.Insert);
                InsertKeyIsOn = true;
            }
        }

        /// <summary>
        /// Bevor der Text eingefügt wird, wird geprüft ob dieser der Maske entspricht.
        /// </summary>
        protected override void OnPreviewTextInput(TextCompositionEventArgs e)
        {
            System.ComponentModel.MaskedTextResultHint tHint;

            if (e.Text.Length == 1)
            {
                this.NewTextIsOk = _mask.VerifyChar(e.Text[0], this.CaretIndex, out tHint);
            }
            else
            {
                int tTestPosition;
                this.NewTextIsOk = _mask.VerifyString(e.Text, out tTestPosition, out tHint);
            }

            base.OnPreviewTextInput(e);
        }

        /// <summary>
        /// Prüfen ob der einzufügende Text den Bestimmungen der Textmaske entspricht
        /// </summary>
        protected override void OnTextInput(TextCompositionEventArgs e)
        {
            string tPreviousText = this.Text;
            if (NewTextIsOk)
            {
                base.OnTextInput(e);
                //Sollte der neu entstandene Text nicht dem Textvorgaben der Textmaske entsprechen, dann wird wieder der alte 
                //bereits bestätigtze Text in die Textbox eingefügt.
                if (_mask.VerifyString(this.Text) == false)
                {
                    this.Text = tPreviousText;
                }

                //Setzten des Aktuellen Indexes??
                while (!_mask.IsEditPosition(this.CaretIndex) && _mask.Length > this.CaretIndex)
                {
                    this.CaretIndex++;
                }
            }
        }

        /// <summary>
        /// Prüft ob der Focus das Control verlassen soll, wenn die Eingabe korrekt war.
        /// </summary>
        protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            if (StayInFocusUntilValid)
            {
                _mask.Clear();
                _mask.Add(this.Text);
                if (_mask.MaskFull)
                {
                    e.Handled = true;
                }
            }
            
            base.OnPreviewLostKeyboardFocus(e);
        }

        #endregion
    }
}
